Documentation

operations/Production Hardening Checklist.md

Production Hardening Checklist

This checklist ensures the Dynaplex platform is properly secured before deploying to Azure. Complete all items before any production or staging deployment.


Quick Reference

Category Critical Items Status
Superset SECRET_KEY required, admin password auto-generated SECRET_KEY must be set
Identity Admin password auto-generated, dev users not seeded Auto-secured
Secrets Azure Key Vault for all secrets Must configure
Network HTTPS, CORS (auto-restricted), firewall rules CORS origins must be configured
Database SSL required, strong passwords Must configure
MQTT Broker authentication Must configure
Logging Warning level in production Auto-configured

1. Authentication & Credentials

1.1 Identity Component (AssettrakUI Login)

Item Status Notes
[x] Secure initial admin password AUTO Production generates random 4-word passphrase (e.g., maple-cedar-brook-flame)
[x] Force password change AUTO Production sets PasswordChangeByDate to past, forcing immediate change
[x] Verify acsis dev user NOT created AUTO The acsis/acsis user is ONLY created when ASPNETCORE_ENVIRONMENT=Development.
[ ] Configure JWT signing key Required Use Azure Key Vault, minimum 256-bit key
[ ] Set appropriate token expiration Recommended Default is 60 minutes, adjust per security requirements

Environment-Aware Password Handling:

  • Development: Uses admin/admin for convenience, no forced password change
  • Production/Staging: Generates secure random 4-word passphrase, forces immediate password change
  • acsis dev user: Only created in Development environment

Post-Deployment Actions (Production):

  1. Check container logs during first startup for the generated password:
    [WRN] INITIAL ADMIN PASSWORD - Save this now! Username: admin, Password: maple-cedar-brook-flame, ID: ...
    
  2. Log in with the generated password
  3. Change password immediately (system will force this on first login)
  4. Optionally create additional admin users and disable the default admin user

Verification:

-- Run against production database to verify dev users don't have weak passwords
SELECT username, email FROM identity.users WHERE username IN ('admin', 'acsis');
-- Should return only 'admin' row (not 'acsis')

1.2 Apache Superset (Analytics Dashboard)

Item Status Notes
[ ] Set SUPERSET_SECRET_KEY CRITICAL Flask session signing key. Deployment will FAIL without this.
[x] Secure admin credentials AUTO Production generates random 4-word passphrase if not explicitly set
[ ] Configure admin email Recommended For password recovery and alerts

Environment-Aware Password Handling:

  • SUPERSET_ENVIRONMENT=development: Uses admin/admin for convenience
  • SUPERSET_ENVIRONMENT=production: If SUPERSET_ADMIN_PASSWORD not set, generates secure random 4-word passphrase

Generate a SECRET_KEY:

# Generate a cryptographically secure key
openssl rand -base64 42

Configuration via Environment Variables:

# Required for production
AZURE_SUPERSET_SECRET_KEY=<output-from-openssl-command>

# Optional - if not set, random password will be generated and logged
AZURE_SUPERSET_ADMIN_USERNAME=<secure-username>
AZURE_SUPERSET_ADMIN_PASSWORD=<strong-password-min-16-chars>
SUPERSET_ADMIN_EMAIL=admin@yourcompany.com

Post-Deployment Actions (if using auto-generated password):

  1. Check container logs during first startup:
    ========================================
    INITIAL ADMIN PASSWORD - SAVE THIS NOW!
    ========================================
    Environment: production
    Username: admin
    Password: maple-cedar-brook-flame
    ========================================
    
  2. Save the password and change it after first login

Security Note: The SUPERSET_SECRET_KEY is used to sign Flask session cookies. If not set in Azure deployments, Superset will fail to start. This prevents accidental deployment with a hardcoded key that could allow session forgery.

1.3 MQTT Broker (HiveMQ)

Item Status Notes
[ ] Configure broker authentication Required Do not use anonymous access
[ ] Set strong credentials Required Used by IoT/BBU components
[ ] Enable TLS for MQTT connections Required Encrypt all MQTT traffic

1.4 PostgreSQL Database

Item Status Notes
[ ] Change default postgres password REQUIRED Never use postgres as password
[ ] Create application-specific users Recommended Principle of least privilege
[ ] Enable SSL connections REQUIRED Azure PostgreSQL requires SSL by default

Configuration:

AZURE_POSTGRES_ADMIN_USER=<admin-username>
AZURE_POSTGRES_ADMIN_PASSWORD=<strong-password-min-16-chars>

2. Secrets Management

2.1 Azure Key Vault Setup

Item Status Notes
[ ] Create Azure Key Vault Required One per environment (staging, production)
[ ] Configure managed identity access Required Container Apps use managed identity
[ ] Store all secrets in Key Vault Required Never in appsettings.json or env vars

Secrets to Store in Key Vault:

  • ConnectionStrings--Acsis - PostgreSQL connection string
  • Jwt--SigningKey - JWT signing key (256-bit minimum)
  • Superset--AdminPassword - Superset admin password
  • Mqtt--Password - MQTT broker password
  • Any API keys for external services

Key Vault Configuration:

# Create Key Vault
az keyvault create --name acsis-kv-prod --resource-group acsis-rg --location eastus

# Add secrets
az keyvault secret set --vault-name acsis-kv-prod --name "ConnectionStrings--Acsis" --value "<connection-string>"
az keyvault secret set --vault-name acsis-kv-prod --name "Jwt--SigningKey" --value "<256-bit-key>"

# Grant access to Container App managed identity
az keyvault set-policy --name acsis-kv-prod \
  --object-id <managed-identity-object-id> \
  --secret-permissions get list

2.2 Configuration Review

Item Status Notes
[ ] No secrets in appsettings.json Required Check all component configs
[ ] No secrets in source control Required Verify .gitignore covers sensitive files
[ ] Environment-specific configs Required Use appsettings.Production.json
[ ] Sensitive values use Key Vault references Required @Microsoft.KeyVault(...) syntax

Files to Review:

# Find potential secrets in config files
grep -r "password\|secret\|apikey\|connectionstring" --include="*.json" engines/

3. Network Security

3.1 HTTPS/TLS Configuration

Item Status Notes
[ ] HTTPS enforced on all services Required Azure Container Apps handles this
[ ] TLS 1.2+ only Required Disable older protocols
[ ] Valid SSL certificates Required Use Azure-managed certs or bring your own
[ ] HSTS enabled Required Strict-Transport-Security header

3.2 CORS Configuration

Item Status Notes
[x] Restrict CORS origins AUTO Production restricts to configured origins only
[x] No wildcard (*) origins AUTO Wildcard only used in Development
[ ] Configure allowed origins Required Set Cors:AllowedOrigins in production config

Environment-Aware CORS Handling:

  • Development: Allows any origin (*) for local testing convenience
  • Production/Staging: Restricts to origins listed in Cors:AllowedOrigins configuration
  • If not configured: No cross-origin requests allowed (safest default)

Configuration in appsettings.Production.json:

{
  "Cors": {
    "AllowedOrigins": [
      "https://yourdomain.com",
      "https://app.yourdomain.com"
    ]
  }
}

Or via Azure App Configuration:

az appconfig kv set --name <config-name> --key "Cors:AllowedOrigins:0" --value "https://yourdomain.com"
az appconfig kv set --name <config-name> --key "Cors:AllowedOrigins:1" --value "https://app.yourdomain.com"

3.3 Firewall & Network Rules

Item Status Notes
[ ] Database firewall configured Required Only allow Container App IPs
[ ] Service-to-service communication internal Required Use Aspire service discovery
[ ] Restrict public endpoints Required Only expose necessary APIs
[ ] Enable Azure DDoS protection Recommended For production workloads

4. Database Security

4.1 PostgreSQL Hardening

Item Status Notes
[ ] SSL mode = require Required Encrypt connections
[ ] Disable public network access Recommended Use private endpoints
[ ] Enable Azure Defender for PostgreSQL Recommended Threat detection
[ ] Configure backup retention Required Minimum 7 days for production
[ ] Enable audit logging Required For compliance and forensics

Connection String for Azure PostgreSQL:

Host=<server>.postgres.database.azure.com;Database=acsis;Username=<user>;Password=<password>;SSL Mode=Require;Trust Server Certificate=false

4.2 Database Access Control

Item Status Notes
[ ] Create read-only user for Superset Recommended Superset only needs SELECT
[ ] Separate users per component Recommended Schema-level isolation
[ ] Regular password rotation Recommended Use Azure Key Vault rotation

5. Application Security

5.1 Security Headers

Item Status Notes
[x] X-Content-Type-Options: nosniff Auto Prevents MIME sniffing
[x] X-Frame-Options: DENY Auto Prevents clickjacking
[x] Content-Security-Policy Auto Restrictive default for APIs
[x] Referrer-Policy Auto strict-origin-when-cross-origin
[x] Permissions-Policy Auto Disables unnecessary browser features
[x] Cache-Control: no-store Auto Prevents caching sensitive responses

Auto-configured in ServiceDefaults (production mode only). Security headers middleware is automatically enabled when IsDevelopment() returns false.

Note: X-XSS-Protection is intentionally omitted as it's deprecated and can cause issues in modern browsers.

5.2 Input Validation

Item Status Notes
[ ] All endpoints validate input Required DataAnnotations on all DTOs
[ ] SQL injection prevention Required Always use parameterized queries
[ ] XSS prevention Required Output encoding in UI

5.3 Error Handling

Item Status Notes
[x] No stack traces in responses Auto ProblemDetails configured in ServiceDefaults
[x] Proper error logging Auto Errors logged server-side, generic response to client
[ ] Custom error pages Recommended User-friendly error messages (UI only)

Auto-configured in ServiceDefaults:

  • Uses RFC 7807 ProblemDetails for consistent error responses
  • Development: Includes request path for debugging
  • Production: Generic error messages only (no sensitive information)
  • TraceId always included for correlation

Logging levels (auto-configured):

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",  // Auto-set by ServiceDefaults in production
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

6. Monitoring & Observability

6.1 Logging Configuration

Item Status Notes
[x] Reduce log verbosity for production Auto ServiceDefaults sets Warning level in non-Development
[ ] No sensitive data in logs Required Mask PII, passwords, etc.
[ ] Enable Application Insights Required Azure native monitoring
[ ] Configure log retention Required Balance cost vs. forensics needs

6.2 Health Checks

Item Status Notes
[ ] All services expose /health Required For Azure monitoring
[ ] Liveness and readiness probes Required Container orchestration
[ ] Database connectivity checks Required Detect connection issues early

6.3 Alerts

Item Status Notes
[ ] Configure Azure alerts Required High error rate, CPU, memory
[ ] Set up PagerDuty/OpsGenie Recommended On-call notifications
[ ] Security event alerts Required Failed logins, unusual activity

7. Container Security

7.1 Image Security

Item Status Notes
[ ] Use official base images Required mcr.microsoft.com/dotnet/aspnet
[ ] Keep images updated Required Regular security patches
[ ] Scan images for vulnerabilities Recommended Azure Container Registry scanning
[ ] Don't run as root Recommended Use non-root user in Dockerfile

7.2 Container App Configuration

Item Status Notes
[ ] Set resource limits Required CPU and memory limits
[ ] Configure scaling rules Required Handle load spikes
[ ] Enable ingress restrictions Recommended IP whitelisting if possible

8. Compliance & Documentation

8.1 Documentation

Item Status Notes
[ ] Document all environment variables Required For ops team
[ ] Runbooks for common issues Recommended Incident response
[ ] Architecture diagram updated Required Current production topology

8.2 Compliance

Item Status Notes
[ ] Data retention policy defined Required GDPR/compliance requirements
[ ] Backup and recovery tested Required Document RTO/RPO
[ ] Penetration testing scheduled Recommended Before go-live

Pre-Deployment Verification Script

Run this script to verify critical security items:

#!/bin/bash
# pre-deployment-check.sh

echo "=== Dynaplex Production Hardening Verification ==="

# Check for hardcoded secrets in config files
echo "Checking for potential secrets in config files..."
SECRETS_FOUND=$(grep -r -i "password.*=.*[a-zA-Z]" --include="*.json" engines/ 2>/dev/null | grep -v "PasswordHash" | grep -v "node_modules" | wc -l)
if [ "$SECRETS_FOUND" -gt 0 ]; then
    echo "WARNING: Potential hardcoded secrets found!"
    grep -r -i "password.*=.*[a-zA-Z]" --include="*.json" components/ 2>/dev/null | grep -v "PasswordHash" | grep -v "node_modules"
else
    echo "OK: No obvious hardcoded secrets found"
fi

# Check Azure parameters file
echo ""
echo "Checking Azure deployment parameters..."
if [ -f "projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/infra/main.parameters.json" ]; then
    if grep -q "AZURE_SUPERSET_ADMIN_PASSWORD" projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/infra/main.parameters.json; then
        echo "OK: Superset admin password is parameterized"
    else
        echo "WARNING: Superset admin password may be hardcoded"
    fi
fi

# Check environment is set correctly
echo ""
echo "Reminder: Ensure ASPNETCORE_ENVIRONMENT is set to 'Production' (not 'Development')"
echo "  - Development users will NOT be seeded when environment != Development"
echo ""
echo "=== Verification Complete ==="

Post-Deployment Verification

After deployment, verify these items:

  1. Test Authentication

    • Identity: Log in with admin/admin, then IMMEDIATELY change the password
    • Identity: Verify acsis user does NOT exist
    • Superset: Uses configured admin credentials (NOT admin/admin)
    • JWT tokens expire as expected
  2. Test Security Headers

    curl -I https://your-production-url/api/health
    # Verify security headers are present
    
  3. Test SSL/TLS

    openssl s_client -connect your-production-url:443 -tls1_2
    # Verify TLS 1.2+ is working
    
  4. Verify Database Connection

    • SSL mode is active
    • Application can connect
    • Superset can query data (read-only)

Emergency Contacts

Role Contact Escalation
Security Team security@acsis.com Immediate
Platform Team platform@acsis.com 30 minutes
On-Call Engineer PagerDuty Via alert

Revision History

Date Author Changes
2025-12-07 Claude Initial creation
2025-12-07 Claude Added SUPERSET_SECRET_KEY requirement; clarified Identity admin/admin seeding behavior (created on first deployment, must change password); updated post-deployment verification steps